using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
using System.Xml;
using ChessKit.ChessLogics;

namespace EmraldClient
{
  [Export(typeof(IStorageService))]
  public class StorageService : IStorageService
  {
    public Dictionary<string, Problem> Storage { get; private set; }
    public void Load(string file)
    {
      if (!File.Exists(file))
      {
        Storage = new Dictionary<string, Problem>();
        return;
      }

      var dic = new Dictionary<string, Problem>();

      using (var reader = new XmlTextReader(file))
      {
        while (reader.Read() && reader.Name != "problems") ;

        while (!reader.EOF)
        {
          while (reader.Read() && reader.Name != "problem") ;
          if (reader.EOF) break;
          var problem = new Problem();
          while (reader.MoveToNextAttribute())
          {
            switch (reader.Name)
            {
              case "fen": problem.AddPosition(reader.Value); break;
              case "id": problem.Id = reader.Value; break;
              case "reversed": problem.IsReversed = XmlConvert.ToBoolean(reader.Value); break;
              case "is-solved": problem.IsSolved = ToNullableBool(reader.Value); break;
              case "ex": problem.Ex = TimeSpan.FromSeconds(XmlConvert.ToDouble(reader.Value)); break;
              case "time": problem.Time = TimeSpan.FromSeconds(XmlConvert.ToDouble(reader.Value)); break;
              case "succ": problem.Succ = XmlConvert.ToDouble(reader.Value); break;
              case "fail": problem.Fail = XmlConvert.ToDouble(reader.Value); break;
              case "tac-rating": problem.TacRating = XmlConvert.ToInt32(reader.Value); break;
              case "tac-rd": problem.TacRD = XmlConvert.ToInt32(reader.Value); break;
              case "pos-rating": problem.PosRating = XmlConvert.ToInt32(reader.Value); break;
              case "pos-rd": problem.PosRD = XmlConvert.ToInt32(reader.Value); break;
            }
          }
          while (reader.Read() && reader.Depth > 1)
          {
            if (reader.NodeType != XmlNodeType.Element) continue;
            if (reader.Name != "move") continue;
            reader.Read();
            problem.AddMove(ParseMove(reader.Value));
          }

          dic[problem.Id] = problem;
        }
      }
      Storage = dic;
    }
    public void Save(string file)
    {
      using (var writer = new XmlTextWriter(file, Encoding.Default))
      {
        writer.WriteStartElement("problems");
        foreach (var p in Storage.Values)
        {
          writer.WriteStartElement("problem");

          writer.WriteAttributeString("fen", p.Position);
          writer.WriteAttributeString("id", p.Id);
          writer.WriteAttributeString("reversed", XmlConvert.ToString(p.IsReversed));
          writer.WriteAttributeString("is-solved", ToString(p.IsSolved));
          writer.WriteAttributeString("ex", XmlConvert.ToString(p.Ex.TotalSeconds));
          writer.WriteAttributeString("time", XmlConvert.ToString(p.Time.TotalSeconds));
          writer.WriteAttributeString("succ", XmlConvert.ToString(p.Succ));
          writer.WriteAttributeString("fail", XmlConvert.ToString(p.Fail));
          writer.WriteAttributeString("tac-rating", XmlConvert.ToString(p.TacRating));
          writer.WriteAttributeString("tac-rd", XmlConvert.ToString(p.TacRD));
          writer.WriteAttributeString("pos-rating", XmlConvert.ToString(p.PosRating));
          writer.WriteAttributeString("pos-rd", XmlConvert.ToString(p.PosRD));

          foreach (var move in p.Moves)
            writer.WriteElementString("move", move.ToString());

          writer.WriteEndElement();
        }
        writer.WriteEndElement();
      }
    }


    private static Move ParseMove(string value)
    {
      var s = value.Split('-');
      return new Move(ChessKit.Common.Position.Parse(s[0]), ChessKit.Common.Position.Parse(s[1]));
    }
    private static string ToString(bool? value)
    {
      if (value == null) return "null";
      if (value == true) return "true";
      return "false";
    }
    private static bool? ToNullableBool(string value)
    {
      switch (value)
      {
        case "null": return null;
        case "true": return true;
        case "false": return false;
      }
      throw new Exception();
    }
  }
}